package cn.zjxiliu.lib_config;

import android.content.Context;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by jessie on 2018/10/27.
 */

public class XMLConfigParser extends AbsConfigParser {

    private static final String TAG = "XMLConfigParser";
    private static final String NODE_BEAN = "bean";
    private static final String NODE_LIST = "list";
    private static final String NODE_LIST_ITEM = "item";
    private static final String NODE_CONFIG = "config";



    public <T> void setFieldValue(String nodeName, T entity, String value){
        try {
            Field field = entity.getClass().getDeclaredField(nodeName);
            field.setAccessible(true);
            String fieldType = field.getType().getName().toLowerCase();
            if(fieldType.contains("string")){
                field.set(entity, value);
            }else if(fieldType.contains("int")){
                field.setInt(entity, Integer.parseInt(value));
            }else if(fieldType.contains("boolean")){
                field.setBoolean(entity, Boolean.parseBoolean(value));
            }else if(fieldType.contains("double")){
                field.setDouble(entity, Double.parseDouble(value));
            }else if(fieldType.contains("long")){
                field.setLong(entity, Long.parseLong(value));
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }


    /**
     * 解析bean对象
     * @param xmlPullParser
     * @param entity
     * @param <T>
     */
    private <T> void parserBean(XmlPullParser xmlPullParser, T entity){
        String beanName = xmlPullParser.getAttributeValue(0);
        try {
            Field field = entity.getClass().getDeclaredField(beanName);
            field.setAccessible(true);
            Object obj = Class.forName(field.getType().getName()).newInstance();
            field.set(entity, obj);
            xmlPullParser.next();
            while (xmlPullParser.getEventType() != XmlPullParser.END_TAG && !NODE_BEAN.equalsIgnoreCase(xmlPullParser.getName())){
                if(xmlPullParser.getEventType() == XmlPullParser.START_TAG){
                    String name = xmlPullParser.getName();
                    setFieldValue(name, obj, xmlPullParser.nextText());
                }
                xmlPullParser.next();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }


    /**
     * 解析列表
     * @param xmlPullParser
     * @param entity
     * @param <T>
     */
    private <T> void parserList(XmlPullParser xmlPullParser, T entity) {
        String beanName = xmlPullParser.getAttributeValue(0);
        try {
            Field field = entity.getClass().getDeclaredField(beanName);
            field.setAccessible(true);
            ParameterizedType listGenericType = (ParameterizedType) field.getGenericType();
            Class<?> item = (Class<?>) listGenericType.getActualTypeArguments()[0];
            List<Object> itemList = new ArrayList();
            field.set(entity, itemList);
            xmlPullParser.next();
            String nodeName = xmlPullParser.getName();
            while (xmlPullParser.getEventType() != XmlPullParser.END_TAG && !NODE_LIST.equalsIgnoreCase(nodeName)){

                if(NODE_LIST_ITEM.equalsIgnoreCase(nodeName)){
                    try {
                        Object itemObj = item.newInstance();
                        xmlPullParser.next();
                        while (xmlPullParser.getEventType() != XmlPullParser.END_TAG && !NODE_LIST_ITEM.equalsIgnoreCase(xmlPullParser.getName())){
                            if(xmlPullParser.getEventType() == XmlPullParser.START_TAG){
                                String name = xmlPullParser.getName();
                                setFieldValue(name, itemObj, xmlPullParser.nextText());
                            }
                            xmlPullParser.next();
                        }
                        itemList.add(itemObj);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    }
                }
                xmlPullParser.next();
                nodeName = xmlPullParser.getName();
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }


    @Override
    public <T> T useParser(Context context, T entity) {
        try {
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            XmlPullParser xmlPullParser = factory.newPullParser();
            xmlPullParser.setInput(AssertsResUtils.getConfigInputStream(context, entity.getClass().getSimpleName() + ".xml"), "UTF-8");
            int eventType = xmlPullParser.getEventType();
            while (eventType != XmlPullParser.END_DOCUMENT) {
                String nodeName = xmlPullParser.getName();
                switch (eventType) {
                    case XmlPullParser.START_TAG: {
                        if(NODE_BEAN.equalsIgnoreCase(nodeName)){
                            parserBean(xmlPullParser, entity);
                        }else if(NODE_LIST.equalsIgnoreCase(nodeName)){
                            parserList(xmlPullParser, entity);
                        }else if(!NODE_CONFIG.equalsIgnoreCase(nodeName)){
                            setFieldValue(nodeName, entity, xmlPullParser.nextText());
                        }
                        break;
                    }
                    default:
                        break;
                }
                eventType = xmlPullParser.next();
            }
            return entity;
        } catch (XmlPullParserException e) {
            e.printStackTrace();
            throw new ConfigParserException(
                    ConfigParserException.FILE_FORMAT_IS_NOT_CORRECT);
        } catch (IOException e) {
            throw new ConfigParserException(ConfigParserException.IO_EXCEPTION);
        }
    }



    @Override
    public ParserType getCurrentParserType() {
        return ParserType.TYPE_XML;
    }


}
